# -*- coding: utf-8 -*-
from __future__ import absolute_import

import os

import salt.config
from tests.support.mock import patch
from tests.support.runtests import RUNTIME_VARS

try:
    import cherrypy

    HAS_CHERRYPY = True
except ImportError:
    HAS_CHERRYPY = False


if HAS_CHERRYPY:
    from tests.support.cptestcase import BaseCherryPyTestCase
    from salt.netapi.rest_cherrypy import app
else:
    from tests.support.unit import TestCase, skipIf

    @skipIf(HAS_CHERRYPY is False, "The CherryPy python package needs to be installed")
    class BaseCherryPyTestCase(TestCase):
        pass

    class BaseToolsTest(BaseCherryPyTestCase):
        pass


class BaseRestCherryPyTest(BaseCherryPyTestCase):
    """
    A base TestCase subclass for the rest_cherrypy module

    This mocks all interactions with Salt-core and sets up a dummy
    (unsubscribed) CherryPy web server.
    """

    def __get_opts__(self):
        return None

    @classmethod
    def setUpClass(cls):
        master_conf = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "master")
        cls.config = salt.config.client_config(master_conf)
        cls.base_opts = {}
        cls.base_opts.update(cls.config)

    @classmethod
    def tearDownClass(cls):
        del cls.config
        del cls.base_opts

    def setUp(self):
        # Make a local reference to the CherryPy app so we can mock attributes.
        self.app = app
        self.addCleanup(delattr, self, "app")

        master_conf = os.path.join(RUNTIME_VARS.TMP_CONF_DIR, "master")
        client_config = salt.config.client_config(master_conf)
        base_opts = {}
        base_opts.update(client_config)

        base_opts.update(
            self.__get_opts__()
            or {
                "external_auth": {
                    "auto": {"saltdev": ["@wheel", "@runner", ".*"]},
                    "pam": {"saltdev": ["@wheel", "@runner", ".*"]},
                },
                "rest_cherrypy": {"port": 8000, "debug": True},
            }
        )

        root, apiopts, conf = app.get_app(base_opts)
        cherrypy.tree.mount(root, "/", conf)
        cherrypy.server.unsubscribe()
        cherrypy.engine.start()

        # Make sure cherrypy does not memleak on its bus since it keeps
        # adding handlers without cleaning the old ones each time we setup
        # a new application
        for value in cherrypy.engine.listeners.values():
            value.clear()
        cherrypy.engine._priorities.clear()

        self.addCleanup(cherrypy.engine.exit)


class Root(object):
    """
    The simplest CherryPy app needed to test individual tools
    """

    exposed = True

    _cp_config = {}

    def GET(self):
        return {"return": ["Hello world."]}

    def POST(self, *args, **kwargs):
        return {"return": [{"args": args}, {"kwargs": kwargs}]}


if HAS_CHERRYPY:

    class BaseToolsTest(BaseCherryPyTestCase):  # pylint: disable=E0102
        """
        A base class so tests can selectively turn individual tools on for testing
        """

        def __get_conf__(self):
            return {
                "/": {"request.dispatch": cherrypy.dispatch.MethodDispatcher()},
            }

        def __get_cp_config__(self):
            return {}

        def setUp(self):
            root = Root()
            patcher = patch.object(root, "_cp_config", self.__get_cp_config__())
            patcher.start()
            self.addCleanup(patcher.stop)

            # Make sure cherrypy does not memleak on its bus since it keeps
            # adding handlers without cleaning the old ones each time we setup
            # a new application
            for value in cherrypy.engine.listeners.values():
                value.clear()
            cherrypy.engine._priorities.clear()

            app = cherrypy.tree.mount(root, "/", self.__get_conf__())
            cherrypy.server.unsubscribe()
            cherrypy.engine.start()
            self.addCleanup(cherrypy.engine.exit)
